home *** CD-ROM | disk | FTP | other *** search
- /*******************************************************************************
- * StandardGetFolder.c *
- * *
- * This little chunk o' code implements a way to let the user choose a *
- * folder to save files in via a StandardFile Dialog. *
- * *
- * Since the code uses the CustomGetFile function and depends on the use of *
- * FSSpec records, it only works under System 7.0 or later. *
- * *
- * And don't forget to include the custom dialog resources ( a 'DITL' and *
- * 'DLOG') in your project. * *
- * *
- * Portions of this code were originally provided by Paul Forrester *
- * (paulf@apple.com) to the think-c internet mailing list in response to my *
- * my question on how to do exactly what this code does. I've added a *
- * couple of features, such as the ability to handle aliased folders and *
- * the programmer definable prompt. I also cleaned and tightened the code, *
- * stomped a couple of bugs, and packaged it up neatly. Bunches of work, *
- * but I learned A LOT about Standard File, the File Manager, the Dialog *
- * Manager, and the Alias Manager. I tried to include in the comments some *
- * of the neat stuff I discovered in my hours of pouring over Inside Mac. *
- * Hope you find it educational as well as useful. *
- *******************************************************************************/
-
- #if 0
- #include <stdio.h>
- #include <String.h>
- #endif
- #include <Script.h>
- #include <Aliases.h>
-
- /*=============================================================================+
- | Function Prototypes |
- +=============================================================================*/
- long GetSFCurDir(void);
-
- short GetSFVRefNum(void);
-
- pascal Boolean MyCustomGetDirectoryFileFilter( CInfoPBPtr myPB,
- Ptr myDataPtr);
-
- void SetButtonTitle ( Handle ButtonHdl,
- Str255 name,
- Rect *ButtonRect);
-
- pascal short MyCustomGetDirectoryDlogHook( short item,
- DialogPtr theDialog,
- Ptr myDataPtr);
-
- char *StrCpy (char *s1, char *s2);
-
- char *StrCat (char *s1, char *s2);
-
-
- /*=============================================================================+
- | Resource IDs |
- +=============================================================================*/
- #define rGetFolderButton 10
- #define rGetFolderMessage 11
- #define kFolderBit 0x0010
- #define rGetFolderDialog 2008
-
-
- /*=============================================================================+
- | Global Variables |
- +=============================================================================*/
- static char gCurrentSelectedFolder [256];
-
-
- /*******************************************************************************
- * StandardGetFolder *
- * *
- * The StandardGetFolder function. You pass it the point where you want the *
- * standard file dialog box drawn, the prompt to display above the file *
- * list, and a pointer to an StandardFileReply record. *
- * *
- * Upon return, the sfFile field of the SFReply record contains the volume *
- * reference number and directory ID that specify the folder the user *
- * chose. It also passes back the name of the chosen folder. The sfGood *
- * field is set to true if the user chose a folder, or false if not. *
- *******************************************************************************/
-
- void StandardGetFolder ( Point where,
- Str255 message,
- StandardFileReply *mySFReply)
-
- {
- SFTypeList theTypeList;
- short numTypes;
- ProcPtr myModalFilter;
- CInfoPBRec pb;
- OSErr err;
- short theItem;
-
- /*-------------------------------------------------------------------------+
- | Setting num types to -1 tells CustomGetFile to pass all files and |
- | folders to the file filter function. |
- +-------------------------------------------------------------------------*/
- numTypes = - 1;
-
- /*-------------------------------------------------------------------------+
- | Copy the prompt to be displayed above the file list into the name field |
- | of the SFReply record. When MyCustomGetDirectoryDlogHook is called for |
- | the first time, it will use this info to draw the prompt. |
- +-------------------------------------------------------------------------*/
- StrCpy ( (char *) mySFReply->sfFile.name, (char *) message);
-
- /*-------------------------------------------------------------------------+
- | Call CustomGetFile. Pass it a pointer to the file filter and dialog |
- | hook functions. Also pass a pointer to mySFReply in the user data field. |
- +-------------------------------------------------------------------------*/
- CustomGetFile( (ProcPtr)MyCustomGetDirectoryFileFilter,
- numTypes,
- theTypeList,
- mySFReply,
- rGetFolderDialog,
- where,
- (ProcPtr)MyCustomGetDirectoryDlogHook,
- NULL,
- (short*)NULL,
- NULL,
- (void *)(mySFReply));
-
- /*-------------------------------------------------------------------------+
- | Ok, now the reply record contains the volume reference number and the |
- | name of the selected folder. We need to use PBGetCatInfo to get the |
- | directory ID of the selected folder. |
- +-------------------------------------------------------------------------*/
- pb.hFileInfo.ioCompletion = NULL;
- pb.hFileInfo.ioNamePtr = mySFReply->sfFile.name;
- pb.hFileInfo.ioVRefNum = mySFReply->sfFile.vRefNum;
- pb.hFileInfo.ioFDirIndex = 0;
- pb.hFileInfo.ioDirID = mySFReply->sfFile.parID;
-
- err = PBGetCatInfoSync( &pb);
-
- /*-------------------------------------------------------------------------+
- | Insert your error handler here. I couldn't think of one so I left it |
- | empty. Works fine without it. |
- +-------------------------------------------------------------------------*/
- if ( err != noErr);
-
- /*-------------------------------------------------------------------------+
- | Copy the directory ID of the selected folder to the sfFile field of the |
- | SFReply record. |
- +-------------------------------------------------------------------------*/
- mySFReply->sfFile.parID = pb.dirInfo.ioDrDirID;
-
- }
-
-
-
- /*******************************************************************************
- * MyCustomGetDirectoryFileFilter *
- * *
- * This is the file filter passed to CustomGetFile. It passes folders only. *
- *******************************************************************************/
- pascal Boolean
- MyCustomGetDirectoryFileFilter( CInfoPBPtr myPB,
- Ptr myDataPtr )
- {
- return( ! (myPB->hFileInfo.ioFlAttrib & kFolderBit ) );
- }
-
-
- /*******************************************************************************
- * MyCustomGetDirectoryDlogHook *
- * *
- * This function lets us process item hits in the GetFolderDialog. We're *
- * only interested if the user hit the selectFolder button. We pass all *
- * other item hits back to ModalDialog. *
- *******************************************************************************/
-
- pascal short MyCustomGetDirectoryDlogHook( short item,
- DialogPtr theDialog,
- Ptr myDataPtr )
- {
-
- WindowPeek dlgPeek;
- Str255 selectedName;
- CInfoPBRec pb;
- short MyCustomGetDirectoryDlogHook;
- OSErr err;
- short itemType;
- Rect itemRect;
- Handle itemHandle;
- Boolean isAlias,
- isFolder;
-
- StandardFileReply *mySFRPtr;
-
-
-
- /*-------------------------------------------------------------------------+
- | Set the return value to defualt to the item that was passed in. |
- +-------------------------------------------------------------------------*/
- MyCustomGetDirectoryDlogHook = item;
-
- /*-------------------------------------------------------------------------+
- | CustomGet calls dialog hook for both main and subsidiary dialog boxes. |
- | Make sure that dialog record indicates that this is the main GetFolder |
- | dialog. |
- +-------------------------------------------------------------------------*/
- dlgPeek = (WindowPeek)(theDialog);
- if ( (OSType)(dlgPeek->refCon) == sfMainDialogRefCon )
- {
- /*---------------------------------------------------------------------+
- | Get a handle to the select folder button, in case we need to change |
- | the label. |
- +---------------------------------------------------------------------*/
- GetDItem(theDialog, rGetFolderButton, &itemType, &itemHandle, &itemRect);
-
- /*---------------------------------------------------------------------+
- | If this is the first time the dialog hook has been called... |
- +---------------------------------------------------------------------*/
- if ( item == sfHookFirstCall )
- {
- /*-----------------------------------------------------------------+
- | Set the prompt displayed above the file list... |
- +-----------------------------------------------------------------*/
- GetDItem ( theDialog, rGetFolderMessage, &itemType, &itemHandle,
- &itemRect );
- mySFRPtr = (StandardFileReply *)(myDataPtr);
- SetIText ( itemHandle, mySFRPtr->sfFile.name );
-
- /*-----------------------------------------------------------------+
- | And the name of the currently selected folder in the select |
- | folder button. |
- +-----------------------------------------------------------------*/
- pb.hFileInfo.ioCompletion = NULL;
- pb.hFileInfo.ioNamePtr = (StringPtr)selectedName;
- pb.hFileInfo.ioVRefNum = GetSFVRefNum();
- pb.hFileInfo.ioDirID = GetSFCurDir();
- pb.hFileInfo.ioFDirIndex = -1;
- err = PBGetCatInfoSync( &pb);
-
- /*-----------------------------------------------------------------+
- | Note that this error return is important! When the dialog hook |
- | is called for the first time, Super Boomerang (and possibly |
- | Norton directory assistance aren't finished doing their |
- | rebounting, so the values returned by GetSFVRefNum and |
- | GetSFCurDir may not be valid, and hence PBGetCatInfo will return |
- | an error. That one took me a while to figure out. |
- +-----------------------------------------------------------------*/
- if ( err != noErr )
- {
- return (MyCustomGetDirectoryDlogHook);
- }
-
- GetDItem(theDialog, rGetFolderButton, &itemType, &itemHandle,
- &itemRect);
- SetButtonTitle( itemHandle, selectedName, &itemRect);
- }
-
- else
- {
- /*-----------------------------------------------------------------+
- | Cast myDataPtr back to a SFReply pointer. |
- +-----------------------------------------------------------------*/
- mySFRPtr = (StandardFileReply *)( myDataPtr );
-
-
- /*-----------------------------------------------------------------+
- | If the selected folder is an alias, resolve it. isFolder will |
- | be set to true if a folder or aliased folder is selected. |
- +-----------------------------------------------------------------*/
- ResolveAliasFile (&(mySFRPtr->sfFile), TRUE, &isFolder, &isAlias);
- if ( (isAlias) && (isFolder) )
- StrCpy( (char *)selectedName, (char *)mySFRPtr->sfFile.name );
-
- /*-----------------------------------------------------------------+
- | If the selected item is a folder or volume, just copy the name |
- | into selectedName... |
- +-----------------------------------------------------------------*/
- else if (( mySFRPtr->sfIsFolder) || (mySFRPtr->sfIsVolume) )
- StrCpy( (char *)selectedName, (char *)mySFRPtr->sfFile.name );
-
- /*-----------------------------------------------------------------+
- | Otherwise, copy the name of the selected item's parent directory |
- | into selectedName. |
- +-----------------------------------------------------------------*/
- else
- {
- pb.hFileInfo.ioCompletion = NULL;
- pb.hFileInfo.ioNamePtr = (StringPtr)selectedName;
- pb.hFileInfo.ioVRefNum = mySFRPtr->sfFile.vRefNum;
- pb.hFileInfo.ioDirID = mySFRPtr->sfFile.parID;
- pb.hFileInfo.ioFDirIndex = -1;
- err = PBGetCatInfoSync( &pb);
- if ( err != noErr)
- return (MyCustomGetDirectoryDlogHook);
- }
-
- /*-----------------------------------------------------------------+
- | If the selected folder has changed since the last call to this |
- | dialog hook function, re-draw the button with the new selected |
- | folder name. |
- +-----------------------------------------------------------------*/
- if ( !EqualString( selectedName, (StringPtr)gCurrentSelectedFolder,
- FALSE, FALSE ) )
- SetButtonTitle(itemHandle, selectedName, &itemRect);
-
- /*-----------------------------------------------------------------+
- | If the user clicked the select folder button, force a cancel and |
- | set the sfGood field of the Reply record to true. |
- +-----------------------------------------------------------------*/
- if (item == rGetFolderButton)
- {
- MyCustomGetDirectoryDlogHook = sfItemCancelButton;
- mySFRPtr->sfGood = TRUE;
- }
-
- }
- }
-
- return (MyCustomGetDirectoryDlogHook );
- }
-
-
-
- /*******************************************************************************
- * SetButtonTitle *
- * *
- * Whenever the selected folder is changed, SetButtonTitle is called to *
- * redraw the get folder button. Pass it a handle to the button, the new *
- * string to be drawn in the button, and a pointer to the rect the button *
- * is drawn within. *
- *******************************************************************************/
- void SetButtonTitle( Handle ButtonHdl,
- Str255 name,
- Rect *ButtonRect )
- {
- short resultCode;
- short width;
- char TmpStr[ 256 ];
-
- StrCpy( gCurrentSelectedFolder, (char*) name );
-
- /*-------------------------------------------------------------------------+
- | Find the width left over in the button after drawing the word 'Select' |
- | the quotation marks. Truncate the new name to this length. |
- +-------------------------------------------------------------------------*/
- width = (ButtonRect->right - ButtonRect->left) -
- (StringWidth((StringPtr)"\pSelect \"\"") +
- CharWidth('J'));
-
- resultCode = TruncString(width, name, smTruncEnd );
- if ( resultCode < 0 );
-
- /*-------------------------------------------------------------------------+
- | Redraw the button. |
- +-------------------------------------------------------------------------*/
- StrCpy ( TmpStr, (char *)"\pSelect \"");
- StrCat ( TmpStr, (char *)name);
- StrCat ( TmpStr, (char *)"\p\"");
- SetCTitle((ControlHandle)(ButtonHdl), (StringPtr)TmpStr );
- ValidRect(ButtonRect);
- }
-
-
- /*******************************************************************************
- * GetSFCurDir, GetSFVRefNum *
- * *
- * The following set of routines are used to access a couple of low memory *
- * globals that are necessary when extending Standard File. One example is *
- * trying to get the current directory while in a file filter. These routines *
- * were used to bottleneck all the low memory usage. If the system one day *
- * supports them with a trap call, then we can easily update these routines. *
- *******************************************************************************/
-
- long GetSFCurDir()
- {
- return( CurDirStore );
- }
-
- short GetSFVRefNum()
- {
- return( - SFSaveDisk);
- }
-
-
- /*******************************************************************************
- * StrCpy *
- * *
- * Just like strcpy except that it takes pascal strings as arguments. * *
- *******************************************************************************/
- char *StrCpy( char *s1, char *s2 )
- {
- BlockMove( s2, s1, 1+*s2);
- return s1;
- }
-
-
-
- /*******************************************************************************
- * StrCat *
- * *
- * Just like strcat except that it takes pascal strings as arguments. * *
- *******************************************************************************/
- char *StrCat( char *s1, char *s2 )
- {
- BlockMove( s2+1, s1+*s1+1, *s2);
- *s1 += *s2;
- return s1;
- }
-
-
-
-
-